package main

import "fmt"

// RemoveAt 删除切片中特定下标的元素。
// T 是泛型类型，约束为任何类型
// 返回新的切片对象,因为可能会有缩容动作,底层的数组可能是新的了,所以需要调用方去接收新的切片
// 使用上与内建的append方法类似
func RemoveAt[T any](s []T, index int) []T {
	if index < 0 || index >= len(s) {
		return s // 索引越界，直接返回原切片
	}

	// 通过子切片的方式删除元素
	// 注意对子切片的append,如果子切片的底层数组后面有元素,则会直接覆盖
	// 以index参数为2为例
	// 底层数组的元素: 0 1 2 3 4 5 6
	// 子切片        : 0 1
	// append后(覆盖): 0 1 3 4 5 6
	s = append(s[:index], s[index+1:]...)

	// 缩容策略：如果切片的容量是长度的两倍以上，则进行缩容
	// TODO: 这里的策略应该可以再优化
	if cap(s) >= 2*len(s) {
		// 创建一个新切片，容量等于当前长度
		newSlice := make([]T, len(s))
		copy(newSlice, s)
		return newSlice
	}

	return s
}

func main() {
	a := []int{1, 2, 3, 4, 5}
	a = RemoveAt(a, 1)             // 删除索引为1的元素（元素2）
	a = RemoveAt(a, 1)             // 删除索引为1的元素（元素3）
	a = RemoveAt(a, 1)             // 删除索引为1的元素（元素4）
	fmt.Println(a, len(a), cap(a)) // 输出(缩容了): [1 5] 2 2

	b := []string{"hello", "world", "foo", "bar"}
	b = RemoveAt(b, 1)             // 删除索引为1的元素（"world"）
	fmt.Println(b, len(a), cap(a)) // 输出: ["hello", "foo", "bar"] 4 5
}
